﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mahanet.Abyss.PathCalculation
{
	public class Line2D
	{
		#region C'tor

		public Line2D(Coordinate2D linePoint, Vector2D direction)
		{
			if (direction.IsZero())
			{
				throw new ArgumentException("direction");
			}

			this.LinePoint = linePoint;
			this.NormalizedDirection = direction.Normalized();
		}

		public Line2D(Coordinate2D firstPoint, Coordinate2D secondPoint)
		{
			if (firstPoint.Equals(secondPoint))
			{
				throw new ArgumentException("direction");
			}

			this.LinePoint = firstPoint;
			this.NormalizedDirection = new Vector2D(firstPoint, secondPoint).Normalized();
		}

		#endregion

		#region Public Properties

		public Coordinate2D LinePoint { get; private set; }
		public Vector2D NormalizedDirection { get; private set; }

		#endregion

		#region Public Methods
		
		public double OrientedDistance(Coordinate2D coordinate)
		{
			Vector2D normalVector = this.NormalizedDirection.Cross();
			Vector2D distanceVector = new Vector2D(this.LinePoint, coordinate);

			return distanceVector.Dot(normalVector);
		}

		public Coordinate2D GetIntersectionPoint(Line2D other)
		{
			// http://en.wikipedia.org/wiki/Line-line_intersection
			double x1 = this.LinePoint.X;
			double x2 = (this.LinePoint + this.NormalizedDirection).X;
			double x3 = other.LinePoint.X;
			double x4 = (other.LinePoint + other.NormalizedDirection).X;

			double y1 = this.LinePoint.Y;
			double y2 = (this.LinePoint + this.NormalizedDirection).Y;
			double y3 = other.LinePoint.Y;
			double y4 = (other.LinePoint + other.NormalizedDirection).Y;
			
			double x1x2det = Determinant(x1, 1, x2, 1);
			double x3x4det = Determinant(x3, 1, x4, 1);
			double y1y2det = Determinant(y1, 1, y2, 1);
			double y3y4det = Determinant(y3, 1, y4, 1);

			double divisor = 
				Determinant(
						x1x2det, y1y2det,
						x3x4det, y3y4det
						);

			if (PrecisionUtils.IsZero(divisor))
			{
				return null;
			}

			double topleftdet = Determinant(x1, y1, x2, y2);
			double bottomleftdet = Determinant(x3, y3, x4, y4);

			double x = Determinant(topleftdet, x1x2det, bottomleftdet, x3x4det) / divisor;
			double y = Determinant(topleftdet, y1y2det, bottomleftdet, y3y4det) / divisor;

			return new Coordinate2D(x, y);
		}

		public bool IsPointOnLine(Coordinate2D coordinate)
		{
			Vector2D distanceVector = new Vector2D(this.LinePoint, coordinate);

			return PrecisionUtils.IsZero(distanceVector.Cross(this.NormalizedDirection));
		}

		#endregion

		#region Private Methods

		private double Determinant(double a, double b, double c, double d)
		{
			return a * d - b * c;
		}

		#endregion
	}
}
